﻿using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using Ipmce.RemoteVoting.Common.Tools.Wcf.Unity.LifetimeManagers;
using Microsoft.Practices.Unity;

namespace Ipmce.RemoteVoting.Common.Tools.Wcf.Unity
{
	/// <summary>
	/// Declares methods that provide a service object or recycle a service object for a Windows Communication Foundation (WCF) service.
	/// </summary>
	public class UnityInstanceProvider : IInstanceProvider
	{
		/// <summary>
		/// Unity container.
		/// </summary>
		private readonly IUnityContainer _container;

		private readonly Type _interfaceType;

		/// <summary>
		/// The <see cref="System.Type"/> to create.
		/// </summary>
		private readonly Type _type;

		/// <summary>
		/// Initializes a new instance of the <see cref="UnityInstanceProvider"/> class with the <see cref="Type"/>
		/// to create and the container to use.
		/// </summary>
		/// <param name="type">The <see cref="Type"/> to create.</param>
		/// <param name="container">The container to use.</param>
		/// <param name="interfaceType"></param>
		public UnityInstanceProvider(Type type, IUnityContainer container, Type interfaceType)
		{
			if (container == null)
				throw new ArgumentNullException("container");

			_type = type;
			_container = container;
			_interfaceType = interfaceType;
		}

		#region IInstanceProvider Members

		/// <summary>
		/// Returns a service object given the specified <see cref="InstanceContext" /> object.
		/// </summary>
		/// <param name="instanceContext">The current <see cref="InstanceContext" /> object.</param>
		/// <returns>
		/// A user-defined service object.
		/// </returns>
		/// <remarks>
		/// Uses the configured Unity container to resolve the service object.
		/// </remarks>
		public object GetInstance(InstanceContext instanceContext)
		{
			return ResolveInstance();
		}

		/// <summary>
		/// Returns a service object given the specified <see cref="T:System.ServiceModel.InstanceContext" /> object.
		/// </summary>
		/// <param name="instanceContext">The current <see cref="T:System.ServiceModel.InstanceContext" /> object.</param>
		/// <param name="message">The message that triggered the creation of a service object.</param>
		/// <returns>
		/// The service object.
		/// </returns>
		/// <remarks>
		/// Uses the configured Unity container to resolve the service object.
		/// </remarks>
		public object GetInstance(InstanceContext instanceContext, Message message)
		{
			return ResolveInstance();
		}

		/// <summary>
		/// Called when an <see cref="T:System.ServiceModel.InstanceContext" /> object recycles a service object.
		/// </summary>
		/// <param name="instanceContext">The service's instance context.</param>
		/// <param name="instance">The service object to be recycled.</param>
		public void ReleaseInstance(InstanceContext instanceContext, object instance)
		{
			instanceContext.Extensions.Remove(UnityInstanceContextExtension.Current);
		}

		#endregion

		private object ResolveInstance()
		{
			if (_interfaceType != null
				&& _container.IsRegistered(_interfaceType))
				return _container.Resolve(_interfaceType);
			return _container.Resolve(_type);
		}
	}
}